#ifndef __DEBUG__
// For printing purposes
#define __DEBUG__ 0
#endif

#include <iostream>
#include <cstdint>

#include "../VAR.hpp"
#include "../TERM.hpp"
#include "../EXPRESSION.hpp"
#include "../ADD.hpp"
#include "../SUB.hpp"
#include "../MIN.hpp"
#include "../MAX.hpp"
#include "../CEIL.hpp"
#include "../FLOOR.hpp"
#include "../GE.hpp"

using namespace std;

// // Global variables for printing and debugging purposes
// int expr_depth = -1;
// int bin_expr_depth = -1;

void test8() {
  constexpr VariableTerm<100> t;

  constexpr VariableTerm<1> t1;
  constexpr VariableTerm<2> t2;
  constexpr VariableTerm<3> t3;
  constexpr VariableTerm<4> t4;
  constexpr VariableTerm<5> t5;
  constexpr VariableTerm<6> pN;
  constexpr VariableTerm<7> pT;
  
  constexpr std::tuple<int, int, int, int, int, int, int, int> 
    test_tuple_min(0, 1, 2, 3, 4, 5, 256, 2048);
  constexpr std::tuple<int, int, int, int, int, int, int, int> 
    test_tuple_max(0, 10, 20, 30, 40, 50, 256, 2048);
  
  {
    constexpr auto expr = -2*t1+t2-3*t3+pN;
    // -2*1 + 2 -3*3 + 256 = 247
    constexpr int v1 = expr.Val(test_tuple_min);
    static_assert(v1 == 247, "ERROR");
    cout << "static_assert(v1 == 247); -> PASSED" << endl;
    // -2*10 + 20 -3*30 + 256 = 166
    constexpr int v2 = expr.Val(test_tuple_max);
    static_assert(v2 == 166, "ERROR");
    cout << "static_assert(v2 == 166); -> PASSED" << endl;
  }

  {
    constexpr auto expr = 
      MIN(-2*t1+t2-3*t3+pN, -2*t1+t2-3*t3+pN+1);
    // -2*1 + 2 -3*3 + 256 = 247
    // MIN(247, 247+1) = 247
    constexpr int v1 = expr.Val(test_tuple_min);
    static_assert(v1 == 247, "ERROR");
    cout << "static_assert(v1 == 247); -> PASSED" << endl;
    // -2*10 + 20 -3*30 + 256 = 166
    // MIN(166, 166+1) = 166
    constexpr int v2 = expr.Val(test_tuple_max);
    static_assert(v2 == 166, "ERROR");
    cout << "static_assert(v2 == 166); -> PASSED" << endl;
  }

  {
    constexpr auto expr = MAX(t1, t2);
    constexpr int v1 = expr.Val(test_tuple_min);
    // cerr << expr << endl;
    static_assert(v1 == 2, "ERROR");
    cout << "static_assert(v1 == 2); -> PASSED" << endl;
    // cerr << v1 << endl;
  }

  {
    constexpr auto expr = 
      MAX(-2*t1+t2-3*t3+pN, -2*t1+t2-3*t3+pN+1);
    // cerr << expr << endl;
    // -2*1 + 2 -3*3 + 256 = 247
    // MAX(247, 247+1) = 248
    constexpr int v1 = expr.Val(test_tuple_min);
    static_assert(v1 == 248, "ERROR");
    cout << "static_assert(v1 == 248); -> PASSED" << endl;
    // cerr << v1 << endl;
    // -2*10 + 20 -3*30 + 256 = 166
    // MAX(166, 166+1) = 167
    constexpr int v2 = expr.Val(test_tuple_max);
    static_assert(v2 == 167, "ERROR");
    cout << "static_assert(v2 == 167); -> PASSED" << endl;
    // cerr << v2 << endl;
  }

  {
    constexpr auto expr = 
      CEIL(MAX(-2*t1+t2-3*t3+pN, -2*t1+t2-3*t3+pN+1), 16);
    // -2*1 + 2 -3*3 + 256 = 247
    // MAX(247, 247+1) = 248
    // CEIL(248, 16) = CEIL(16 * 15 + 8, 16) = 16
    constexpr int v1 = expr.Val(test_tuple_min);
    static_assert(v1 == 16, "ERROR");
    cout << "static_assert(v1 == 16); -> PASSED" << endl;
    // -2*10 + 20 -3*30 + 256 = 166
    // MAX(166, 166+1) = 167
    // CEIL(167, 16) = 11
    constexpr int v2 = expr.Val(test_tuple_max);
    static_assert(v2 == 11, "ERROR");
    cout << "static_assert(v2 == 11); -> PASSED" << endl;
  }

  {
    constexpr auto expr = 
      FLOOR(MAX(-2*t1+t2-3*t3+pN, -2*t1+t2-3*t3+pN+1), 16);
    // -2*1 + 2 -3*3 + 256 = 247
    // MAX(247, 247+1) = 248
    // FLOOR(248, 16) = CEIL(16 * 15 + 8, 16) = 15
    constexpr int v1 = expr.Val(test_tuple_min);
    static_assert(v1 == 15, "ERROR");
    cout << "static_assert(v1 == 15); -> PASSED" << endl;
    // -2*10 + 20 -3*30 + 256 = 166
    // MAX(166, 166+1) = 167
    // CEIL(167, 16) = 10
    constexpr int v2 = expr.Val(test_tuple_max);
    static_assert(v2 == 10, "ERROR");
    cout << "static_assert(v2 == 10); -> PASSED" << endl;
  }


  {
    constexpr auto expr = 
      (-2*t1+t2-3*t3+pN >= -2*t1+t2-3*t3+pN+1);
    // -2*1 + 2 -3*3 + 256 = 247
    // 247 >= 247+1 ?  --> false
    constexpr bool v1 = expr.Val(test_tuple_min);
    static_assert(!v1, "ERROR");
    cout << "static_assert(!v1); -> PASSED" << endl;
    cout << "static_assert(!v1) passed" << endl;
    // -2*10 + 20 -3*30 + 256 = 166
    // MAX(166, 166+1) = 167
    // CEIL(167, 16) = 10
    constexpr bool v2 = expr.Val(test_tuple_max);
    static_assert(!v2, "ERROR");
    cout << "static_assert(!v2); -> PASSED" << endl;
  }

}

int main (void) {
  test8();
}
